/** 
 * @(#)DVMRPRoutingTable.java
 */

/**
 * @author Robert Denda&Didier Gutacker
 * @version 1.1
 * @see RoutingTable
 * @see Table 
 *
 * original author Robert Denda, changes made by Didier Gutacker
 */

import java.util.Vector;
import java.awt.Frame;


public class DVMRPRoutingTable extends RoutingTable {

  debug DEBUG = new debug();
   
  public DVMRPRoutingTable( int nodeID ) {
    super();
  } // DVMRPRoutingTable.DVMRPRoutingTable

  public final boolean update( Link incomingLink, int destinationID, int metric, Link l, int infinity ) {

    boolean updated;

    int newMetric = metric + incomingLink.getWeight();
    DVMRPRoutingEntry entry = (DVMRPRoutingEntry) lookUp( destinationID );
    //dvr conditions.......... trace this .... and why? and how?  parent-child relationship lies here ..track that one...
    /*
     * condition A: if the route is new and the **adjusted metric(new metric)*** is less than infinity, the route should be
     * added..
     */
    if ( entry == null && metric < infinity ) {
      entry = new DVMRPRoutingEntry( this, destinationID, newMetric, incomingLink, infinity );
      entryChangeGuard.acquire();
      entries.addElement( entry );
      entryChangeGuard.release();
      updateTableWindow();
      return true;
      /*
       * condition B: if the route already exists, several checks must be performed....
       */
    } else if ( entry != null ) {
    	  //1. if received metric < infinity
         if ( metric < infinity ) {
    	    //1.a   if adjusted metric  > existing metric
	        if ( newMetric > entry.metric ) {
		         // check to see if the same neighbor is reporting the route. 
	           if ( entry.l.equals( incomingLink ) ) {
	                entryChangeGuard.acquire();
	                 //if so, update the route metric and ??schedule a flash update??** containing the route
	    
	                entry.metric = newMetric;
	                
	                //??
	                 if ( entry.metric > entry.infinity ) {
	                       entry.infinity = entry.metric + 1;
	                  } // if
	                 
	                 
	                 entryChangeGuard.release();
	               //  return true;
	    
	            /*
	             * if the same neighbor is not reporting the route remove the dependency from the routing entries..??if the neighbor was considered
	             * a downstream dependent neighbor, the dependency is canceled.
	             */  
	           } /* else {
	        	          //weflag=1;
	        	        
	                     entryChangeGuard.acquire();
	                     entry.removeChild( incomingLink );
	                     entryChangeGuard.release();
	                  } //end  if (entry.l.equals( incomingLink))  */
	        /*1.b
	         * if adjusted metric < existing metric and if the neighbor reporting the route is different, update
	         * the upstream neighbor in the routing table.Schedule a flash update containing the route to downstream
	         * neighbors and a ** flash poison update containing the route ***? should be sent upstream indicating a change in
	         * downstream dependency (even if it is on the same upstream interface).   
	         */
	        } else if ( newMetric < entry.metric ) {
	                 entryChangeGuard.acquire();
	                 entry.children = null;
	                 updated = entry.update( destinationID, newMetric, incomingLink, entry.infinity );
	                 entryChangeGuard.release();
	                 updateTableWindow();
	                 return updated;
	         /* 1.c
	          * if adjusted metric == existing metric  **one or two conditions missing here** 
	          * if the neighbor reporting the route has a lower IP address than the existing  upstream neighbor
	          * then switch to this neighbor as the best route.Schedule a **flash update containing the route to
	          * downstream neighbors and a ***flash poison update containing the route should be sent upstream 
	          * indicating a change in downstream dependency (even if it is on the same upstream interface).--This
	          * should be unnecessary update due to sole purpose of using child links---***  
	          */
	          }/* else {
	                   if ( !entry.l.equals( incomingLink ) ) {
	                          if ( getNextHopRouter( incomingLink ) < getNextHopRouter( (Link) entry.l ) ) {
	                                entryChangeGuard.acquire();
	                                entry.children = null;
	                                //incomingLink.tiebreak = true;
	                                entry.l = incomingLink;
	                                
	                                entryChangeGuard.release();
	                                updateTableWindow();
	                                return true;
	                           } else {
	                        	   
	                        	        entryChangeGuard.acquire();
	                        	        incomingLink.tiebreaklink.addElement(incomingLink);
	                        	        incomingLink.tiebreaknode.addElement(getNextHopRouter(incomingLink));
	                                   // incomingLink.tiebreak = true;
	                                   // entry.removeChild( incomingLink );
	                                    entryChangeGuard.release();
	                                   // updateTableWindow();
	                                   } // if  
	                    } // end if ( !entry.l.equals ( incomingLink ) )
	                 } // end else */
	  /* 2.
	   *   if received metric == infinity then
	   *     a. Next hop == existing next hop then
	   *         schedule a flash update containing the route to all interfaces for which 
	   *         you are the designated  forwarder(Dominant Router) or have downstream  dependents
	   * 
	   */      
      } else if ( metric == infinity ) {
	               if ( getNextHopRouter( incomingLink ) == getNextHopRouter( (Link) entry.l ) ) {
	                     entryChangeGuard.acquire();
	                     updated = entry.update( destinationID, infinity, incomingLink, infinity );
	                     entryChangeGuard.release();
	                     updateTableWindow();
	                     return updated;
	                } // if
	 
        }  //end else if (metric == infinity ) */
         
      /*  we can remove this segment of code for removing the child links??
       * this condition is  to add **Subordinate routers** as children for a source
         ref: RFC 1075 internet draft dvmrp v3.09
         if metric is in between infinity and 2*infinity will the router which 
         sent the report becomes a dependent router or that incoming link becomes a 
         child link ?? 
         
         3. 
       *   if  infinity < metric && metric < 2 * infinity  then
       *       a. ???neighbor on downstream interface???
       *            if the sending neighbor is considered to be on a downstream interface
       *            for that route then the neighbor is to be registered as a downstream
       *            dependent router (****Subordinate Router*****) for that route.
       */ 
      /* else if ( infinity < metric && metric < 2 * infinity ) {
	             if ( ! incomingLink.equals( entry.l ) ) {
	                 entryChangeGuard.acquire();
	                 entry.addChild( incomingLink );
	                 entryChangeGuard.release();
	                 updateTableWindow();
	              } // if
        } // end else if ( infinity < metric && metric < 2 * infinity )   */
    } // end else if ( entry != null )  
    
     else {
              DEBUG.out( "Severe Error:  !!!", this );
          } // end I if
            return false;
  } // end method  DVMRPRoutingTable.update

  public final void registerLink( Link l ) {
    entryChangeGuard.acquire();
    for ( int i = 0; i < entries.size(); i += 1 ) {
      ( (DVMRPRoutingEntry) entries.elementAt( i ) ).addChild( l );
    } // for
    entryChangeGuard.release();
  } // DVMRPRoutingTable.registerLink

  public final void deregisterLink( Link l, int infinity ) {
    entryChangeGuard.acquire(); // set Lock
    for ( int i = 0; i < entries.size(); i += 1 ) {
      DVMRPRoutingEntry entry = (DVMRPRoutingEntry) entries.elementAt( i );
      if ( entry.l.equals( l ) ) {
	entry.metric = infinity;
      } // if
      entry.removeChild( l );
    } // for
    entryChangeGuard.release(); // release Lock
  } // DVMRPRoutingTable.deregisterLink


  public final int getNextHopRouter( Link l ) {
    int routerID = -1;
    int distance = -1;
    
    entryChangeGuard.acquire();
    for ( int i = 0; i < entries.size(); i += 1 ) {
	if ( ( (DVMRPRoutingEntry) entries.elementAt( i ) ).l.equals( l ) ) {
	  if ( ( routerID == -1 ) || ( distance >= ( (DVMRPRoutingEntry) entries.elementAt( i ) ).metric ) ) {
	    routerID = ( (DVMRPRoutingEntry) entries.elementAt( i ) ).destinationID;
	    distance = ( (DVMRPRoutingEntry) entries.elementAt( i ) ).metric;
	  } // if
	} // if
    } // for
    entryChangeGuard.release();
    return routerID;
  } // DVMRPRoutingTable.getNextHopRouter

  public final Vector getForwardInterfaces( int destinationID ) {

    Vector interfaces = new Vector();
    entryChangeGuard.acquire();
    DVMRPRoutingEntry entry = (DVMRPRoutingEntry) lookUp( destinationID );
    if ( entry == null ) {
      return null;
    } // if
    Vector childList = entry.getChildList();
    if ( childList == null ) {
      entryChangeGuard.release();
      return null;
    } // if
    for ( int i = 0; i < childList.size(); i += 1 ) {
      DVMRPRoutingEntry nextHop = (DVMRPRoutingEntry) lookUp( getNextHopRouter( (Link) childList.elementAt(i) ) );
      if ( nextHop != null ) {
        if ( ! nextHop.pruned ) {
	  interfaces.addElement( (Link) childList.elementAt(i) );
        } else {
	  nextHop.pruningTimer -= 1;
	  if ( nextHop.pruningTimer <= 0 ) {
	    nextHop.pruned = false;
	    interfaces.addElement( (Link) childList.elementAt(i) );
	  } // if
        } // if
      } // if
      } // for
    entryChangeGuard.release();
    updateTableWindow();
    return interfaces;
  } // DVMRPRoutingTable.getForwardInterfaces
  
  public final Vector getUnprunedLinks() 
  {
	  Vector links = new Vector();
	    entryChangeGuard.acquire();
	   // DVMRPRoutingEntry entry = (DVMRPRoutingEntry) lookUp( destinationID );
	  //  if ( entry == null ) {
	  //    return null;
	  //  } // if
	    Vector all_links = getLinks();
	    if ( all_links == null ) {
	      entryChangeGuard.release();
	      return null;
	    } // if
	    for ( int i = 0; i < all_links.size(); i += 1 ) 
	    {
	      DVMRPRoutingEntry nextHop = (DVMRPRoutingEntry) lookUp( getNextHopRouter( (Link) all_links.elementAt(i) ) );
	      if ( nextHop != null ) 
	      {
	        if ( ! nextHop.pruned ) 
	        {
		      links.addElement( (Link) all_links.elementAt(i) );
	        } 
	        else 
	        {
		       nextHop.pruningTimer -= 1;
		       if ( nextHop.pruningTimer <= 0 ) 
		       {
		        nextHop.pruned = false;
		        links.addElement( (Link) all_links.elementAt(i) );
		       } // if
	        } // if
	      } // if
	    } // for
	    entryChangeGuard.release();
	   // updateTableWindow();
	    return links;
  }

  public final Vector getLinks() {
    Vector links = new Vector();
    entryChangeGuard.acquire();
    for ( int i = 0; i < entries.size(); i += 1 ) {
      if ( !links.contains( ( (DVMRPRoutingEntry) entries.elementAt( i ) ).l ) ) {
	links.addElement( ( (DVMRPRoutingEntry) entries.elementAt( i ) ).l ) ;
      } // if
    } // for
    entryChangeGuard.release();
    return links;
  } // DVMRPRoutingTable.getLinks

  public final void setPrunedFlag( int nodeID ) {
    entryChangeGuard.acquire();
    DVMRPRoutingEntry entry = (DVMRPRoutingEntry) lookUp( nodeID );
    entry.pruned = true;
    entry.pruningTimer = 5;
    entryChangeGuard.release();
  } // DVMRPRoutingTable.setPrunedFlag

  public final void unsetPrunedFlag( int nodeID ) {
    entryChangeGuard.acquire();
    ((DVMRPRoutingEntry) lookUp( nodeID )).pruned = false;
    entryChangeGuard.release();
    updateTableWindow();
  } // DVMRPRoutingTable.unsetPrunedFlag

  public final boolean getPrunedFlag( int nodeID ) {
    entryChangeGuard.acquire();
    boolean returnValue = ((DVMRPRoutingEntry) lookUp( nodeID )).pruned;
    entryChangeGuard.release();
    return returnValue;
  } // DVMRPRoutingTable.getPrunedFlag

  public final void unpruneEntries() {
    entryChangeGuard.acquire();
    for ( int i = 0; i < entries.size(); i += 1 ) {
      ((DVMRPRoutingEntry) entries.elementAt(i) ).pruningTimer = 0;
      ((DVMRPRoutingEntry) entries.elementAt(i) ).pruned = false;
    } // for
    entryChangeGuard.release();
    updateTableWindow();
  } // DVMRPRoutingTable.unpruneEntries

  public final void increasePrunedTimer( int nodeID ) {
    entryChangeGuard.acquire();
    DVMRPRoutingEntry entry = (DVMRPRoutingEntry) lookUp( nodeID );
    if ( entry.pruned ) {
      entry.pruningTimer--;
      if ( entry.pruningTimer == 0 ) {
	entry.pruned = false;
      } // if
    } // if
    entryChangeGuard.release();
    updateTableWindow();
  } // DVMRPRoutingTable.increasePrunedTimer

  public void createTableWindow( Node n, Network net ) {
    if ( tableWindow == null ) {
      tableWindow = new DVMRPRoutingTableWindow( this, n, net );
    } else {
      tableWindow.repaint();
    } // if
  } // DVMRPRoutingTable.createTableWindow

 
} // DVMRPRoutingTable





